package cn.pugle.quartz.cluster;

import org.quartz.DisallowConcurrentExecution;
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.PersistJobDataAfterExecution;
import org.quartz.Scheduler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.MethodInvoker;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

/**
 * 通过MethodInvoker调用的Job。 <br>
 * 从jobDataMap中获取invoker的名字， 去InvokerRepository找到invoker <br>
 * Created by tzp on 16-11-29.
 */
public class MethodInvokingJob implements Job {
    private static Logger logger = LoggerFactory.getLogger(MethodInvokingJob.class);


    public static final String JOB_INVOKER_BEAN_NAME_KEY = "jobInvokerBeanName";

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        JobDataMap jobDataMap = context.getMergedJobDataMap();
        String jobDetailBeanName = (String) jobDataMap.get(JOB_INVOKER_BEAN_NAME_KEY);
        try {
            Scheduler scheduler = context.getScheduler();
            logger.info("MethodInvokingJob is execute {} by scheduler {}", jobDetailBeanName, scheduler.getSchedulerInstanceId());

            MethodInvoker invoker = InvokerRepository.getInvoker(jobDetailBeanName);
            context.setResult(invoker.invoke());
        } catch (InvocationTargetException ex) {
            if (ex.getTargetException() instanceof JobExecutionException) {
                throw (JobExecutionException) ex.getTargetException();
            } else {
                throw new JobExecutionException(jobDetailBeanName, ex.getTargetException());
            }
        } catch (Exception ex) {
            throw new JobExecutionException(jobDetailBeanName, ex);
        }
    }

    /**
     * Extension of the MethodInvokingJob, implementing the StatefulJob interface.
     * Quartz checks whether or not jobs are stateful and if so,
     * won't let jobs interfere with each other.
     */
    @PersistJobDataAfterExecution
    @DisallowConcurrentExecution
    public static class StatefulMethodInvokingJob extends MethodInvokingJob {
        //没有代码，只是多两个注解
    }

    public static class InvokerRepository {
        private static final Map<String, MethodInvoker> invokersMap = new HashMap<>();

        public static void addInvoker(String name, MethodInvoker invoker) {
            if (invokersMap.containsKey(name)) {
                throw new RuntimeException("error while add Invoker. Duplicate name " + name);
            } else {
                invokersMap.put(name, invoker);
            }
        }

        public static MethodInvoker getInvoker(String name) {
            return invokersMap.get(name);
        }

    }
}
